#include <iostream>
#include <string>
#include <vector>
#include <boost/filesystem.hpp>
#include <boost/regex.hpp>
#include "util.hpp"

const std::string src_path = "data/input";
const std::string output = "data/raw_html/raw.txt";

struct DocInfo
{
    /* data */
    std::string title;   // 标题
    std::string content; // 内容
    std::string url;     // 文档在官网中的url
};

bool EnumFile(const std::string &src_path, std::vector<std::string> *files_list);
bool ParseHtml(const std::vector<std::string> &file_list, std::vector<DocInfo> *result);
bool SaveHtml(const std::vector<DocInfo> results, const std::string &output);

int main()
{
    // 将路径文件名提取出来 后续使用
    std::vector<std::string> file_list;
    if (!EnumFile(src_path, &file_list))
    {
        std::cerr << "enum file name error!" << std::endl;
        return 1;
    }

    // 通过路径打开文件 将文件的标题，内容，url提取出来
    std::vector<DocInfo> results;
    if (!ParseHtml(file_list, &results))
    {
        std::cerr << "parse html error" << std::endl;
        return 2;
    }

    // 第三步: 把解析完毕的各个⽂件内容，写⼊到output,按照\3作为每个⽂档的分割符
    if (!SaveHtml(results, output))
    {
        std::cerr << "sava html error" << std::endl;
        return 3;
    }

    return 0;
}

bool EnumFile(const std::string &src_path, std::vector<std::string> *files_list)
{
    // 按文件名进行存储到files_list
    // c++11不能使用filesystem使用boost库里的filesystem
    namespace fs = boost::filesystem;

    // 使用fs::exists测试文件存不存在
    if (!fs::exists(src_path))
    {
        std::cerr << "文件" << src_path << "不存在" << std::endl;
        return false;
    }

    // 测试src_path是不是文件夹
    if (!fs::is_directory(src_path))
    {
        std::cerr << src_path << "不是文件夹" << std::endl;
        return false;
    }

    fs::recursive_directory_iterator end;          // 结束条件
    fs::recursive_directory_iterator it(src_path); // 初始化后通常指向目录中的第一个条目
    // 把文件路径和名读取到files_list中
    // int count = 0;
    while (it != end)
    {
        if (!(it->path().extension() == ".html"))
        {
            ++it;
            continue;
        }

        files_list->push_back(it->path().string());

        // std::cout << count++ << "push:" << it->path().string() << std::endl;
        ++it;
    }
    return true;
}

bool ParseTitle(const std::string &result, std::string *title)
{
    // 方案1：正则表达式的方式
    // if (title == nullptr)
    //     return false; // 防止空指针

    // // 正则：匹配 <title> 和 </title> 中间的内容
    // boost::regex pattern("<title>([^<]*)</title>", boost::regex::icase);

    // // 结果输出到 std::smatch match;
    // boost::smatch match;
    // if (!boost::regex_search(result, match, pattern))
    // {
    //     std::cerr << "regex_search err" << std::endl;
    //     return false;
    // };
    // // std::cout << "Full match: " << match[0] << std::endl; // <title>My Page Title</title>
    // // std::cout << "Title text: " << match[1] << std::endl; // My Page Title
    // *title = match[1].str();

    // 方案2：手动提取
    std::size_t begin;
    std::size_t end;
    begin = result.find("<title>");
    if (begin == std::string::npos)
    {
        return false;
    };
    end = result.find("</title>");
    if (end == std::string::npos)
    {
        return false;
    }

    begin += std::string("<title>").size();

    if (end < begin)
    {
        return false;
    }
    *title = result.substr(begin, end - begin);

    return true;
}

bool ParseContent(const std::string &result, std::string *content)
{
    // 使用状态机读取 内容
    enum status
    {
        LABLE,
        CONTENT
    };
    enum status s = LABLE;
    for (char c : result)
    {
        switch (s)
        {
        case LABLE:
            if (c == '>')
            {
                s = CONTENT;
            }
            break;
        case CONTENT:
            if (c == '<')
            {
                s = LABLE;
            }
            else
            {
                // 把\n去掉，方便后面的操作
                // 在用 content->back() 之前要保证 content 不为空，否则会越界崩溃。
                // 去掉换行符 \n
                // 去掉连续空格（只保留一个空格）
                // 如果当前是空格并且前一个字符已经是空格，就跳过。
                if (c == '\n' || (c == ' ' && !content->empty() && content->back() == ' '))
                    continue;
                content->push_back(c);
            }
            break;
        default:
            break;
        }
    }

    return true;
}

bool ParseUrl(const std::string &file_path, std::string *url)
{
    // 原网页
    // https://www.boost.org/doc/libs/1_89_0/doc/html

    std::string url_head = "https://www.boost.org/doc/libs/latest/doc/html";
    std::string url_tail = file_path.substr(src_path.size());

    *url += url_head;
    *url += url_tail;
    return true;
}

void TestGet(const DocInfo &doc)
{
    // 测试有没有提取出来
    std::cout << doc.title << std::endl;
    std::cout << doc.content << std::endl;
    std::cout << doc.url << std::endl;
}

bool ParseHtml(const std::vector<std::string> &file_list, std::vector<DocInfo> *results)
{
    // 解析 HTML
    for (const std::string &file : file_list)
    {
        // 读取文件,把文件内容写入result
        std::string result;
        if (!ns_ntil::FileUtil::ReadFile(file, &result))
        {
            continue;
        }

        // 解析指定文件，提取title
        DocInfo doc;
        if (!ParseTitle(result, &doc.title))
        {
            continue;
        }

        // 解析指定文件，提取content
        if (!ParseContent(result, &doc.content))
        {
            continue;
        }

        // 解析指定的文件路径，构建url
        if (!ParseUrl(file, &doc.url))
        {
            continue;
        }

        // 测试有没有提取出来
        // TestGet(doc);
        // break;

        // 一定是完成了解析才能到达这一步，把解析完成的结果doc插入vector
        results->push_back(std::move(doc)); // 本质会发生拷贝，效率会降低,使用move把数据变为右值数据
    }
    return true;
}

bool SaveHtml(const std::vector<DocInfo> results, const std::string &output)
{
    // 把解析完毕的各个⽂件内容，写⼊到output,按照\3作为每个⽂档的分割符
    std::ofstream fw(output, std::ios::binary);
    if (!fw.is_open())
    {
        std::cerr << "open fire err" << std::endl;
    }
    for (auto &file : results)
    {
        std::string out;
        out += file.title;
        out += "\3";
        out += file.content;
        out += "\3";
        out += file.url;
        out += "\n";
        fw.write(out.c_str(), out.size());
    }
    return true;
}